#Anaconda/envs/NetOrchestr python
# -*- coding: utf-8 -*-

'''
SfceSagin.py
==============

.. module:: SfceSagin
  :platform: Windows
  :synopsis: NetOrchestr 仿真器

.. moduleauthor:: WangXi

简介
----

存在交叉流量影响的多处理节点模型、时延指标尽量真实 (PID 控制器 + 前馈控制)

SFC 仿真场景

             |                     ---  controller(aimdelay)   ---                     |
             |---------------------|--------------|--------------|---------------------|
             |                   Uav0           Uav1           Uav2                    |
             |   ------ [40]ms  ------  [10]ms ------  [10]ms ------  [40]ms  ------   |
    1pkt/ms--|--| ue0  |------>| vnf0 |------>| vnf1 |------>| vnf2 |------->| ue1  |--|--70ms
             |  |      |       |'s0v0'|       |'s0v1'|       |'s0v2'|        |      |  |
             |  |      |       |'s1v0'|       |'s1v1'|       |      |        |      |  |
             |   ------         ------         ------         ------          ------   |
             |                  ^                   |                                  |
             |   ------ [40]ms  |                   | [40]ms  ------                   |
    1pkt/ms--|--| ue2  |---------                   -------->| ue3  |------------------|--60ms
             |  |      |                                     |      |                  |
             |  |      |                                     |      |                  |
             |   ------                                       ------                   |
             |-------------------------------------------------------------------------|

'''


import os
import random
import numpy as np
from astropy import units as u
from astropy.time import Time

# 可以通过设置随机数种子使得生成的随机场景相同

random.seed(0)   # Python的随机性
os.environ['PYTHONHASHSEED'] = str(0)    # 设置Python哈希种子，为了禁止hash随机化，使得实验可复现
np.random.seed(0)   # numpy的随机性

from netorchestr.envir.base import ONet

net = ONet('SfceSagin',
           seed_id=0,
           work_dir=os.path.dirname(__file__))

init_time = Time("2021-07-22 00:00:00")

LANDMARK_LOCATIONS = {
    'P1上空': [113, 30.00, 4.0],
    'P1地面': [113, 30.00, 0.0],
    'P2上空': [113, 30.04, 4.0],
    'P2地面': [113, 30.04, 0.0],
    'P3上空': [113, 30.08, 4.0],
    'P3地面': [113, 30.08, 0.0],
    # 适用于通信距离 150km 以内的场景
}

# region step1:基底网络设施 --------------------------------------------

from netorchestr.envir.node.uav import UavServerPlatform
from netorchestr.envir.node.ue import UeWithSfcReq
from netorchestr.envir.mobility import Trajectory
from netorchestr.envir.physicallayer import RadioMedium

uav_0 = UavServerPlatform(name='Uav0', 
                          init_time=init_time, 
                          init_gps=LANDMARK_LOCATIONS['P1上空'],
                          ip_pool_str='192.168.0.0/24',
                          node_resource_dict={'cpu':8, 'ram':16*u.GB, 'rom':256*u.GB},
                          link_resource_dict={'band':100*u.Mbit/u.s})
# uav_0_trajectory = Trajectory(name='U0Tr', avg_speed=(100*u.km/u.h).to(u.m/u.s).value)
# uav_0.mobiusTraj.set_trajectory(uav_0_trajectory, init_time)
net.add_module(uav_0)

uav_1 = UavServerPlatform(name='Uav1', 
                          init_time=init_time, 
                          init_gps=LANDMARK_LOCATIONS['P2上空'],
                          ip_pool_str='192.168.1.0/24',
                          node_resource_dict={'cpu':8, 'ram':16*u.GB, 'rom':256*u.GB},
                          link_resource_dict={'band':100*u.Mbit/u.s})
# uav_1_trajectory = Trajectory(name='U1Tr', avg_speed=(100*u.km/u.h).to(u.m/u.s).value)
# uav_1.mobiusTraj.set_trajectory(uav_1_trajectory, init_time)
net.add_module(uav_1)

uav_2 = UavServerPlatform(name='Uav2', 
                          init_time=init_time, 
                          init_gps=LANDMARK_LOCATIONS['P3上空'],
                          ip_pool_str='192.168.2.0/24',
                          node_resource_dict={'cpu':8, 'ram':16*u.GB, 'rom':256*u.GB},
                          link_resource_dict={'band':100*u.Mbit/u.s})
# uav_2_trajectory = Trajectory(name='U2Tr', avg_speed=(100*u.km/u.h).to(u.m/u.s).value)
# uav_2.mobiusTraj.set_trajectory(uav_2_trajectory, init_time)
net.add_module(uav_2)

# endregion

# region step2:服务发起用户 ---------------------------------------------------

ue_0 = UeWithSfcReq(name='Ue0', init_time=init_time, init_gps=LANDMARK_LOCATIONS['P1地面'])
net.add_module(ue_0)

ue_1 = UeWithSfcReq(name='Ue1', init_time=init_time, init_gps=LANDMARK_LOCATIONS['P3地面'])
net.add_module(ue_1)

ue_2 = UeWithSfcReq(name='Ue2', init_time=init_time, init_gps=LANDMARK_LOCATIONS['P1地面'])
net.add_module(ue_2)

ue_3 = UeWithSfcReq(name='Ue3', init_time=init_time, init_gps=LANDMARK_LOCATIONS['P2地面'])
net.add_module(ue_3)

ue_4 = UeWithSfcReq(name='Ue4', init_time=init_time, init_gps=LANDMARK_LOCATIONS['P1地面'])
net.add_module(ue_4)

ue_5 = UeWithSfcReq(name='Ue5', init_time=init_time, init_gps=LANDMARK_LOCATIONS['P2地面'])
net.add_module(ue_5)

ue_6 = UeWithSfcReq(name='Ue6', init_time=init_time, init_gps=LANDMARK_LOCATIONS['P1地面'])
net.add_module(ue_6)

ue_7 = UeWithSfcReq(name='Ue7', init_time=init_time, init_gps=LANDMARK_LOCATIONS['P2地面'])
net.add_module(ue_7)

ue_8 = UeWithSfcReq(name='Ue8', init_time=init_time, init_gps=LANDMARK_LOCATIONS['P1地面'])
net.add_module(ue_8)

ue_9 = UeWithSfcReq(name='Ue9', init_time=init_time, init_gps=LANDMARK_LOCATIONS['P2地面'])
net.add_module(ue_9)

radio_medium = RadioMedium('RadioMedium')
net.add_module(radio_medium)
net.ready_for_medium(radio_medium)

# endregion

# region step3:微服务部署模板--------------------------------------------------

from netorchestr.envir.node.controller.mano.vnfm import VnfManager,VnfEm

vnfManager = VnfManager()
vnfEm_template = VnfEm(type = 'type0',
                       resource_limit = {'cpu':1, 
                                         'ram':64*u.MB, 
                                         'rom':128*u.MB},
                       rate_per_core = 10)
vnfManager.add_vnf_into_templates(vnfEm_template)
vnfEm_template = VnfEm(type = 'type1',
                       resource_limit = {'cpu':1,
                                         'ram':64*u.MB,
                                         'rom':128*u.MB},
                       rate_per_core = 10)
vnfManager.add_vnf_into_templates(vnfEm_template)
vnfEm_template = VnfEm(type = 'type2',
                       resource_limit = {'cpu':1,
                                         'ram':64*u.MB,
                                         'rom':128*u.MB},
                       rate_per_core = 10)
vnfManager.add_vnf_into_templates(vnfEm_template)

# endregion

# region step4:用户服务请求------------------------------------------------

from netorchestr.envir.applications.ueapp import SfcReq

sfc_req_0 = SfcReq(id=0,
                   start_time=0*u.ms,
                   end_time=1000*u.ms,
                   sfc_type='Ue2Ue', 
                   sfc_vnfs_type=["type0","type1","type2"],
                   sfc_vnfs_shared=[True,True,True],
                   sfc_qos={'latency':100*u.ms,'overrate':0.1},
                   sfc_trans_model={'type':'Stable',
                                    'interval':1*u.ms, 
                                    'payload_size':1*u.kbit},
                   sfc_end_point=[ue_0,ue_1]) 

sfc_req_1 = SfcReq(id=1,
                   start_time=0*u.ms,
                   end_time=200*u.ms,
                   sfc_type='Ue2Ue', 
                   sfc_vnfs_type=["type0","type1"],
                   sfc_vnfs_shared=[True,True],
                   sfc_qos={'latency':90*u.ms,'overrate':0.1},
                   sfc_trans_model={'type':'Poisson',
                                    'interval':1*u.ms, 
                                    'payload_size':1*u.kbit},
                   sfc_end_point=[ue_2,ue_3]) 

sfc_req_2 = SfcReq(id=2,
                   start_time=0*u.ms,
                   end_time=400*u.ms,
                   sfc_type='Ue2Ue', 
                   sfc_vnfs_type=["type0","type1"],
                   sfc_vnfs_shared=[True,True],
                   sfc_qos={'latency':90*u.ms,'overrate':0.1},
                   sfc_trans_model={'type':'Poisson',
                                    'interval':1*u.ms, 
                                    'payload_size':1*u.kbit},
                   sfc_end_point=[ue_4,ue_5]) 

sfc_req_3 = SfcReq(id=3,
                   start_time=0*u.ms,
                   end_time=600*u.ms,
                   sfc_type='Ue2Ue', 
                   sfc_vnfs_type=["type0","type1"],
                   sfc_vnfs_shared=[True,True],
                   sfc_qos={'latency':90*u.ms,'overrate':0.1},
                   sfc_trans_model={'type':'Poisson',
                                    'interval':1*u.ms, 
                                    'payload_size':1*u.kbit},
                   sfc_end_point=[ue_6,ue_7]) 

sfc_req_4 = SfcReq(id=4,
                   start_time=0*u.ms,
                   end_time=800*u.ms,
                   sfc_type='Ue2Ue', 
                   sfc_vnfs_type=["type0","type1"],
                   sfc_vnfs_shared=[True,True],
                   sfc_qos={'latency':90*u.ms,'overrate':0.1},
                   sfc_trans_model={'type':'Poisson',
                                    'interval':1*u.ms, 
                                    'payload_size':1*u.kbit},
                   sfc_end_point=[ue_8,ue_9]) 

sfcReqlist = [sfc_req_0, sfc_req_1, sfc_req_2, sfc_req_3, sfc_req_4]

#  endregion

# region step5:控制器及求解器 -----------------------------------------------------

from netorchestr.envir.node.controller.mano.trace import TRACE_RESULT, TRACE_NFVI, Trace
from netorchestr.envir.node.controller.mano import ControllerMano
from netorchestr.envir.node.controller.mano.solver_deploy import SolverDeploySharedBase

solver_deploy = SolverDeploySharedBase
solver_deploy = solver_deploy(name=solver_deploy.__name__)

controller = ControllerMano(name='Controller', 
                            net=net, 
                            vnfManager=vnfManager, 
                            sfcReqlist=sfcReqlist)
controller.set_solver_deploy(solver_deploy)

if solver_deploy.name == 'SolverDeployGcnDrl':
    solver_deploy.load_param(solver_deploy.get_newest_model_file(os.path.dirname(__file__)))
else:
    pass

net.add_module(controller)

# endregion

# region step6:场景可视化检查
# （非必须步骤）

# net_draw_area = MobilityBase.get_rectangular_area(LANDMARK_LOCATIONS_FLOOD)
# net.get_area_gif(time_start=min(sfc_start_time_group),
#                  time_until=max(sfc_end_time_group)+1*u.min,
#                  time_accuracy=1 * u.min,
#                  draw_region=[net_draw_area['left_top'][0]-0.1,
#                               net_draw_area['right_bottom'][0]+0.1,
#                               net_draw_area['right_bottom'][1]-0.1,
#                               net_draw_area['left_top'][1]+0.1],
#                  filename=f"{net.name}_area.gif")
# net.get_area_gif(time_start=min(sfc_start_time_group),
#                  time_until=max(sfc_end_time_group)+1*u.min,
#                  time_accuracy=1 * u.min,
#                  draw_region=[-180,180,-90,90],
#                  filename=f"{net.name}_global.gif")

# endregion



# region step7:开始仿真-------------------------------------------------------

net.sim_time_accuracy = 1 * u.s
net.run(until=1100*u.ms)

# endregion

# region step8:仿真结果分析 --------------------------------------------------------

from netorchestr.common.util import DataAnalysis

DataAnalysis.getResult(TRACE_RESULT.filename)

# endregion

# region step9:保存求解器参数 --------------------------------------------------------
# （非必须步骤）

solver_deploy.save_param()

# endregion

